package com.log4jviewer.ui.views;

import org.eclipse.jface.viewers.IStructuredContentProvider;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.FontData;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Table;
import org.eclipse.swt.widgets.TableItem;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.log4jviewer.domain.LogEvent;
import com.log4jviewer.domain.LogField;
import com.log4jviewer.domain.LogList;
import com.log4jviewer.domain.LogListListener;
import com.log4jviewer.ui.preferences.additional.AdditionalPreferencePage;
import com.log4jviewer.ui.preferences.additional.AdditionalPreferencePageListener;
import com.log4jviewer.ui.preferences.additional.ColumnModel;
import com.log4jviewer.ui.preferences.additional.LogDisplayPreferences;
import com.log4jviewer.ui.preferences.additional.LogLevelDisplayModel;

/**
 * Class provides content for Log Table in Log4jViewer view.
 * 
 * @author <a href="mailto:rd.ryly@gmail.com">Ruslan Diachenko</a>
 */
public class LogViewContentProvider implements IStructuredContentProvider, AdditionalPreferencePageListener,
        LogListListener {

    private final Logger logger = LoggerFactory.getLogger(getClass());

    private Table table;

    private LogList logList;

    public LogViewContentProvider(final LogList logList) {
        this.logList = logList;

        AdditionalPreferencePage.addListener(this);
        logList.addListener(this);
    }

    @Override
    public void dispose() {
        AdditionalPreferencePage.removeListener(this);
        logList.removeListener(this);
    }

    @Override
    public void inputChanged(final Viewer viewer, final Object oldInput, final Object newInput) {
        this.table = ((TableViewer) viewer).getTable();
    }

    @Override
    public Object[] getElements(final Object inputElement) {
        return logList.getFilteredLogs().toArray();
    }

    @Override
    public void handleAdditionalPreferenceEvent(final AdditionalPreferencePage event, final ColumnModel[] columnItems) {
        // Update colors if they were changed in Color Preferences.
        int tableItemCount = table.getItemCount();

        for (int i = 0; i < tableItemCount; i++) {
            updateTableItem(i);
        }
        logger.debug("Table item's display was updated.");
    }

    @Override
    public void addLog(final LogEvent newLog) {
        int itemIndex = logList.getFilteredLogsNum() - 1;
        logger.debug("Item index = {}", itemIndex);

        new TableItem(table, SWT.NONE);
        updateTableItem(itemIndex);
        logger.debug("A new log was added into log table.");
    }

    @Override
    public void handleLogBufferOverflowEvent(final LogList event, final int bufferOverflowSize) {
        if (table.getItemCount() > 0) {
            logger.debug("Log buffer overflow! New logs number: {}", bufferOverflowSize);
            table.remove(0, bufferOverflowSize - 1);
            logger.debug("LogTable rows [0, {}] were removed.", bufferOverflowSize - 1);
        }
    }

    public void updateTableItem(final int itemIndex) {
        logger.debug("Log table item number: {}", table.getItemCount());
        TableItem tableItem = table.getItem(itemIndex);
        LogLevelDisplayModel[] levelDisplayModel = LogDisplayPreferences.getLogColorSettings()
                .getLogLevelDisplayModelList();

        for (int i = 0; i < table.getColumnCount(); i++) {
            String columnName = table.getColumn(i).getText();

            if (columnName.equals(LogField.LEVEL.getName())) {
                String currentLogLevel = logList.getFilteredLog(itemIndex).getLevel();

                for (int levelIndex = 0; levelIndex < levelDisplayModel.length; levelIndex++) {
                    String logLevel = levelDisplayModel[levelIndex].getLevelName();

                    if (logLevel.equals(currentLogLevel)) {
                        applyLogDisplayPreferences(tableItem, levelDisplayModel[levelIndex]);

                        if (LogDisplayPreferences.getLogColorSettings().isLevelImageInsteadOfText()) {
                            Image image = levelDisplayModel[levelIndex].getImage();
                            tableItem.setImage(i, image);
                            tableItem.setText("");
                            logger.debug("Level image usage.");
                        } else {
                            Image image = null;
                            tableItem.setImage(image);
                            tableItem.setText(i, currentLogLevel);
                            logger.debug("Level text usage.");
                        }
                        break;
                    }
                }

            } else if (columnName.equals(LogField.CATEGORY.getName())) {
                String logFieldInfo = logList.getFilteredLog(itemIndex).getCategoryName();
                tableItem.setText(i, logFieldInfo);

            } else if (columnName.equals(LogField.MESSAGE.getName())) {
                tableItem.setText(i, logList.getFilteredLog(itemIndex).getMessage());

            } else if (columnName.equals(LogField.LINE.getName())) {
                String logFieldInfo = logList.getFilteredLog(itemIndex).getLineNumber();
                tableItem.setText(i, logFieldInfo);

            } else if (columnName.equals(LogField.DATE.getName())) {
                String logFieldInfo = logList.getFilteredLog(itemIndex).getDate();
                tableItem.setText(i, logFieldInfo);

            } else if (columnName.equals(LogField.NDC.getName())) {
                String logFieldInfo = logList.getFilteredLog(itemIndex).getNdc();
                tableItem.setText(i, logFieldInfo);

            } else if (columnName.equals(LogField.THROWABLE.getName())) {
                String logFieldInfo = logList.getFilteredLog(itemIndex).getThrowableInfo();
                tableItem.setText(i, logFieldInfo);
            }
        }
    }

    private void applyLogDisplayPreferences(final TableItem tableItem, final LogLevelDisplayModel levelDisplayModel) {
        Color foreground = new Color(Display.getCurrent(), levelDisplayModel.getForeground());
        tableItem.setForeground(foreground);

        Color background = new Color(Display.getCurrent(), levelDisplayModel.getBackground());
        tableItem.setBackground(background);

        FontData[] fontData = tableItem.getFont().getFontData();

        for (int i = 0; i < fontData.length; ++i) {
            fontData[i].setHeight(LogDisplayPreferences.getLogFontSettings().getFontSize());
        }
        final Font newFont = new Font(Display.getCurrent(), fontData);
        tableItem.setFont(newFont);
        logger.debug("Level's font = {}", LogDisplayPreferences.getLogFontSettings().getFontSize());
    }
}
